package com.ahui.mallchat.common.user.service.impl;

import com.ahui.mallchat.common.common.event.UserBlackEvent;
import com.ahui.mallchat.common.common.event.UserRegisterEvent;
import com.ahui.mallchat.common.common.utils.AssertUtil;
import com.ahui.mallchat.common.common.utils.sensitiveWord.SensitiveWordBs;
import com.ahui.mallchat.common.user.dao.BlackDao;
import com.ahui.mallchat.common.user.dao.UserBackpackDao;
import com.ahui.mallchat.common.user.dao.UserDao;
import com.ahui.mallchat.common.user.domain.dto.SummeryInfoDTO;
import com.ahui.mallchat.common.user.domain.entity.Black;
import com.ahui.mallchat.common.user.domain.entity.IpInfo;
import com.ahui.mallchat.common.user.domain.entity.User;
import com.ahui.mallchat.common.user.domain.entity.UserBackpack;
import com.ahui.mallchat.common.user.domain.enums.BlackTypeEnum;
import com.ahui.mallchat.common.user.domain.vo.req.BlackReq;
import com.ahui.mallchat.common.user.domain.vo.req.ModifyNameReq;
import com.ahui.mallchat.common.user.domain.vo.req.SummeryInfoReq;
import com.ahui.mallchat.common.user.domain.vo.resp.UserInfoResp;
import com.ahui.mallchat.common.user.service.UserService;
import com.ahui.mallchat.common.user.service.adapter.UserAdapter;
import com.ahui.mallchat.common.user.service.cache.UserCache;
import com.ahui.mallchat.common.user.service.cache.UserSummaryCache;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

import static com.ahui.mallchat.common.user.domain.enums.ItemEnum.MODIFY_NAME_CARD;

/**
 * Author:ahui
 * Description:
 * Date: 2024/7/21 13:21
 */
@Service
public class UserServiceImpl implements UserService {

    private static final Logger log = LoggerFactory.getLogger(UserServiceImpl.class);
    @Autowired
    private UserDao userDao;

    @Autowired
    private UserBackpackDao userBackpackDao;

    @Autowired
    private ApplicationEventPublisher applicationEventPublisher;

    @Autowired
    private BlackDao blackDao;

    @Autowired
    private UserSummaryCache userSummaryCache;

    @Autowired
    private UserCache userCache;

    @Autowired
    private SensitiveWordBs sensitiveWordBs;

    @Override
    @Transactional
    public Long register(User insert) {
        userDao.save(insert);
        // 用户注册的事件(发送物品) this是事件发出者
        applicationEventPublisher.publishEvent(new UserRegisterEvent(this, insert));
        return insert.getId();
    }

    @Override
    public UserInfoResp getUserInfo(Long uid) {
        User user = userDao.getById(uid);
        Integer modifyNameCount = userBackpackDao.getCountByValidItemId(uid, MODIFY_NAME_CARD.getId());
        return UserAdapter.buildUserInfo(user,modifyNameCount);
    }

    /**
     * 有两条修改事件，需要添加一个数据库事务
     * 针对所有异常生效 @Transactional(rollbackFor = Exception.class)
     * @param uid
     * @param req
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void modifyName(Long uid, ModifyNameReq req) {
        //判断名字是不是重复
        String newName = req.getName();
        //判断名字中有没有敏感词
        AssertUtil.isFalse(sensitiveWordBs.hasSensitiveWord(newName),"名字中包含敏感词，请重新输入");
        User oldName = userDao.getByName(newName);
        AssertUtil.isEmpty(oldName,"名字已经被抢占了，请换一个哦~~");
        //判断改名卡够不够
        UserBackpack modifyNameItem = userBackpackDao.getFirstValidItem(uid, MODIFY_NAME_CARD.getId());
        AssertUtil.isNotEmpty(modifyNameItem,"改名卡不够了,等后续活动送改名卡吧");
        //使用改名卡
        boolean success = userBackpackDao.invalidItem(modifyNameItem.getId());
        if (success){
            //改名
            userDao.modifyName(uid,req.getName());
            //删除缓存
            userCache.userInfoChange(uid);
        }
    }



    @Override
    @Transactional(rollbackFor = Exception.class)
    public void black(BlackReq req) {
        Long uid = req.getUid();
        Black user = new Black();
        user.setType(BlackTypeEnum.UID.getType());
        user.setTarget(uid.toString());
        blackDao.save(user);
        User byId = userDao.getById(uid);
        blackIp(Optional.ofNullable(byId.getIpInfo()).map(IpInfo::getCreateIp).orElse(null));
        blackIp(Optional.ofNullable(byId.getIpInfo()).map(IpInfo::getUpdateIp).orElse(null));
        applicationEventPublisher.publishEvent(new UserBlackEvent(this,byId));
    }

    @Override
    public List<SummeryInfoDTO> getSummeryUserInfo(SummeryInfoReq req) {
        //需要前端同步的uid
        List<Long> uidList = getNeedSyncUidList(req.getReqList());
        //加载用户信息
        Map<Long, SummeryInfoDTO> batch = userSummaryCache.getBatch(uidList);
        return req.getReqList().stream()
                .map(a -> batch.containsKey(a.getUid()) ? batch.get(a.getUid()) : SummeryInfoDTO.skip(a.getUid()))
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
    }

    private List<Long> getNeedSyncUidList(List<SummeryInfoReq.infoReq> reqList) {
        List<Long> needSyncUidList = new ArrayList<>();
        List<Long> userModifyTime = userCache.getUserModifyTime(reqList.stream().map(SummeryInfoReq.infoReq::getUid).collect(Collectors.toList()));
        for (int i = 0; i < reqList.size(); i++) {
            SummeryInfoReq.infoReq infoReq = reqList.get(i);
            Long modifyTime = userModifyTime.get(i);
            if (Objects.isNull(infoReq.getLastModifyTime()) || (Objects.nonNull(modifyTime) && modifyTime > infoReq.getLastModifyTime())){
                needSyncUidList.add(infoReq.getUid());
            }
        }
        return needSyncUidList;
    }

    private void blackIp(String ip) {
        if (StringUtils.isBlank(ip)){
            return;
        }
        try {
            Black user = new Black();
            user.setType(BlackTypeEnum.IP.getType());
            user.setTarget(ip);
            blackDao.save(user);
        }catch (Exception e){
            log.error("不可重复拉黑",e);
        }


    }
}
